﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CLFramework.Caching;
using System.Net;
using System.Transactions;
using CLFramework.Infrastructure.Validation;
using CLFramework.Utility;

namespace CLFramework.Infrastructure
{
    public delegate Dictionary<TKey, TEntity> GetEntityListFromDatabaseDelegate<TKey, TEntity, TBase>
        (TKey[] keys, Dictionary<TKey, TBase> entities) where TEntity : TBase;

    public class EntityManager<TKey, TEntity, TBase> where TEntity : TBase, IEntity<TKey>
    {
        //private static readonly LogProvider Log = LogProvider.GetLogProvider(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        string cacheKey = string.Format("EntityManager-{0}-{1}-{2}", typeof(TKey).FullName, typeof(TEntity).FullName, typeof(TBase).FullName);
        CacheEntityTable<TKey, TEntity> entitiesCache = null;
        GetEntityListFromDatabaseDelegate<TKey, TEntity, TBase> getEntityListFromDatabase;
        GetEntitiesFromDatabaseDelegate<TKey, TBase> getEntityBaseListFromEntityBaseManager;

        public EntityManager(
            GetEntityListFromDatabaseDelegate<TKey, TEntity, TBase> getEntityListFromDatabase,
            GetEntitiesFromDatabaseDelegate<TKey, TBase> getEntityBaseListFromEntityBaseManager
            )
        {
            entitiesCache
                = CacheManager.GetCacheEntityTable<TKey, TEntity>(cacheKey, 180, 10000);
            this.getEntityListFromDatabase = getEntityListFromDatabase;
            this.getEntityBaseListFromEntityBaseManager = getEntityBaseListFromEntityBaseManager;
        }

        public CacheEntityTable<TKey, TEntity> EntitiesCache
        {
            get { return entitiesCache; }
            set { entitiesCache = value; }
        }

        public TEntity GetEntity(TKey key, bool cacheable)
        {
            TKey[] keys = new TKey[] { key };
            List<TEntity> entities = GetEntities(keys, cacheable);
            if (entities.Count == 1)
                return entities[0];
            return default(TEntity);
        }

        public List<TEntity> GetEntities(TKey[] keys, bool entityListCacheable)
        {
            Dictionary<TKey, TEntity> entities = new Dictionary<TKey, TEntity>();
            return GetEntities(keys, ref entities, entityListCacheable);
        }

        public List<TEntity> GetEntities(TKey[] postIds
            , ref Dictionary<TKey, TEntity> entities, bool entityListCacheable)
        {
            return EntitiesCache.GetEntities(postIds, ref entities, entityListCacheable,
                new GetEntitiesFromDatabaseDelegate<TKey, TEntity>(GetEntityListFromDatabase));
        }

        public PagedResult<TEntity> GetEntitySet(List<TKey> keys, int pageIndex, int pageSize
            , bool entitiesCacheable)
        {
            return EntitiesCache.GetEntitySet(keys, pageIndex,
                pageSize, entitiesCacheable, new GetEntitiesFromDatabaseDelegate<TKey, TEntity>(GetEntityListFromDatabase));
        }

        void GetEntityListFromDatabase(TKey[] keys
            , ref Dictionary<TKey, TEntity> sectionPosts
            )
        {
            Dictionary<TKey, TBase> posts = new Dictionary<TKey, TBase>();
            getEntityBaseListFromEntityBaseManager(keys, ref posts);
            sectionPosts = getEntityListFromDatabase(keys, posts);
        }

        public ModelResult<TEntity> CreateEntity(TEntity entity
            , Func<TEntity, ValidationState> validate
            , Func<TBase, ModelResult<TBase>> createEntityBase
            , Func<TEntity, ValidationState> createEntity, bool enableMSDTC)
        {
            ValidationStateDictionary validationStateDictionary = new ValidationStateDictionary();

            ValidationState state = validate(entity);
            validationStateDictionary.Add(typeof(TEntity), state);

            if (!validationStateDictionary.IsValid)
                return new ModelResult<TEntity>(validationStateDictionary);

            ModelResult<TEntity> entityResult;
            if (enableMSDTC)
            {
                // 使用事务以保证原子性
                //
                try
                {
                    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
                    {
                        ModelResult<TBase> status = createEntityBase(entity);
                        if (status.IsValid)
                        {
                            state.Merge(createEntity(entity));
                            if (state.IsValid) // success
                            {
                                scope.Complete();
                            }
                            entityResult = new ModelResult<TEntity>(entity, validationStateDictionary);
                        }
                        else
                        {
                            entityResult = new ModelResult<TEntity>(entity, status.ValidationState);
                        }
                        scope.Dispose();
                        return entityResult;
                    }
                }
                catch (Exception ex)
                {
                    ValidationState vs = new ValidationState();
                    vs.AddValidationError(entity, "CreateEntity", ex);
                    ValidationStateDictionary vsd = new ValidationStateDictionary(typeof(TEntity), vs);
                    entityResult = new ModelResult<TEntity>(vsd);

                    Log.Exception(ex);
                }
            }
            else
            {
                // 不使用事务
                //
                try
                {
                    ModelResult<TBase> status = createEntityBase(entity);
                    if (status.IsValid)
                    {
                        state.Merge(createEntity(entity));
                        entityResult = new ModelResult<TEntity>(entity, validationStateDictionary);
                    }
                    else
                    {
                        entityResult = new ModelResult<TEntity>(entity, status.ValidationState);
                    }
                }
                catch (Exception ex)
                {
                    ValidationState vs = new ValidationState();
                    vs.AddValidationError(entity, "CreateEntity", ex);
                    ValidationStateDictionary vsd = new ValidationStateDictionary(typeof(TEntity), vs);
                    entityResult = new ModelResult<TEntity>(vsd);

                    Log.Exception(ex);
                }
            }

            return entityResult;
        }


        public ModelResult<TEntity> UpdateEntity(TEntity entity
             , Func<TEntity, ValidationState> validate
           , Func<TBase, ModelResult<TBase>> updateEntityBase
            , Func<TEntity, ValidationState> updateEntity, bool enableMSDTC)
        {
            ValidationStateDictionary validationStateDictionary = new ValidationStateDictionary();

            ValidationState state = validate(entity);
            validationStateDictionary.Add(typeof(TEntity), state);

            if (!validationStateDictionary.IsValid)
                return new ModelResult<TEntity>(validationStateDictionary);

            ModelResult<TEntity> entityResult;
            if (enableMSDTC)
            {
                // 使用事务以保证原子性
                //
                try
                {
                    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
                    {
                        ModelResult<TBase> status = updateEntityBase(entity);
                        if (status.IsValid)
                        {
                            EntitiesCache.Remove(entity.EntityKey);
                            state.Merge(updateEntity(entity));
                            if (state.IsValid) // success
                            {
                                scope.Complete();
                            }
                            entityResult = new ModelResult<TEntity>(entity, validationStateDictionary);
                        }
                        else
                        {
                            entityResult = new ModelResult<TEntity>(entity, status.ValidationState);
                        }
                        scope.Dispose();
                        return entityResult;
                    }
                }
                catch (Exception ex)
                {
                    ValidationState vs = new ValidationState();
                    vs.AddValidationError(entity, "UpdateEntity", ex);
                    ValidationStateDictionary vsd = new ValidationStateDictionary(typeof(TEntity), vs);
                    entityResult = new ModelResult<TEntity>(vsd);

                    Log.Exception(ex);
                }
            }
            else
            {
                // 不使用事务
                //
                try
                {
                    ModelResult<TBase> status = updateEntityBase(entity);
                    if (status.IsValid)
                    {
                        EntitiesCache.Remove(entity.EntityKey);
                        state.Merge(updateEntity(entity));
                        entityResult = new ModelResult<TEntity>(entity, validationStateDictionary);
                    }
                    else
                    {
                        entityResult = new ModelResult<TEntity>(entity, status.ValidationState);
                    }
                }
                catch (Exception ex)
                {
                    ValidationState vs = new ValidationState();
                    vs.AddValidationError(entity, "UpdateEntity", ex);
                    ValidationStateDictionary vsd = new ValidationStateDictionary(typeof(TEntity), vs);
                    entityResult = new ModelResult<TEntity>(vsd);

                    Log.Exception(ex);
                }
            }

            return entityResult;
        }
    }
}
